//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.tuiofx.internal.base;

import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;

import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.event.EventTarget;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.text.Text;
import javafx.stage.Window;
import org.tuiofx.Configuration;
import org.tuiofx.internal.gesture.TuioTouchPoint;
import org.tuiofx.internal.util.Util;

public class TargetSelection {
    private final Scene scene;
    private final Configuration config;

    public TargetSelection(Scene scene, Configuration config) {
        this.scene = scene;
        this.config = config;
        this.addTuioFXCanvas(scene);
    }

    public EventTarget selectTargetNode(TuioTouchPoint touchPoint) {
        double screenX = touchPoint.getScreenX();
        double screenY = touchPoint.getScreenY();
        double sceneX = touchPoint.getSceneX();
        double sceneY = touchPoint.getSceneY();
        boolean continueLoop = true;
        Node startNode = this.scene.getRoot();
        Iterator<Window> windows = Window.getWindows().iterator();

        while (windows.hasNext() && continueLoop) {
            final Window window = (Window) windows.next();
            if (this.scene.getWindow() != window) {
                double touchScreenMinX = screenX;
                double touchScreenMinY = screenY;
                if (this.config.isUseIndirectInputDevice()) {
                    touchScreenMinX = screenX + Util.getOffsetX(this.scene);
                    touchScreenMinY = screenY + Util.getOffsetY(this.scene);
                }

                double screenMinX = window.getX() + window.getScene().getX();
                double screenMinY = window.getY() + window.getScene().getY();
                double screenMaxX = screenMinX + window.getWidth();
                double screenMaxY = screenMinY + window.getHeight();
                if (Util.isWithIn(touchScreenMinX, screenMinX, screenMaxX) && Util.isWithIn(touchScreenMinY, screenMinY, screenMaxY)) {
                    continueLoop = false;
                    sceneX = touchScreenMinX - Util.getOffsetX(window.getScene());
                    sceneY = touchScreenMinY - Util.getOffsetY(window.getScene());
                    startNode = window.getScene().getRoot();
                    if (!(startNode instanceof TuioFXCanvas)) {
                        Platform.runLater(new Runnable() {
                            public void run() {
                                TargetSelection.this.addTuioFXCanvas(window.getScene());
                            }
                        });
                    }
                }
            }
        }

        Node node = this.hitTest((Parent) startNode, startNode, sceneX, sceneY);
        return node;
    }

    public boolean isNodeInTheCurrentScene(Node targetNode) {
        return targetNode.getScene() != this.scene;
    }

    private Node hitTest(final Parent root, Node node, final double x, final double y) {
        Node targetNode = pickNode(root, x, y);

        if (targetNode != null) {
            targetNode = this.getPickableNode(targetNode);
        }

        return targetNode;
    }

    private Node getPickableNode(Node targetNode) {
        String pickIfMultiTouch = (String) targetNode.getProperties().get("pickIfMultiTouch");
        String isTouchTransparent = (String) targetNode.getProperties().get("isTouchTransparent");
        if (pickIfMultiTouch != null) {
        }

        if ((pickIfMultiTouch == null || !pickIfMultiTouch.equals("false")) && (isTouchTransparent == null || !isTouchTransparent.equals("true"))) {
            return !(targetNode instanceof Text) || !(targetNode.getParent() instanceof Button) && !(targetNode.getParent() instanceof Label) ? targetNode : this.getPickableNode(targetNode.getParent());
        } else {
            return this.getPickableNode(targetNode.getParent());
        }
    }

    private void updateTouchPointXY(TuioTouchPoint touchPoint, Scene targetScene) {
        double sceneX;
        double sceneY;
        if (this.config.isUseIndirectInputDevice()) {
            sceneX = touchPoint.getScreenX() + this.scene.getWindow().getX() + this.scene.getX();
            sceneY = touchPoint.getScreenY() + this.scene.getWindow().getY() + this.scene.getY();
            touchPoint.setScreenX(sceneX);
            touchPoint.setScreenY(sceneY);
        }

        sceneX = touchPoint.getScreenX() - (targetScene.getWindow().getX() + targetScene.getX());
        sceneY = touchPoint.getScreenY() - (targetScene.getWindow().getY() + targetScene.getY());
        touchPoint.setSceneX(sceneX);
        touchPoint.setSceneY(sceneY);
    }

    private void addTuioFXCanvas(Scene scene) {
        if (!(scene.getRoot() instanceof TuioFXCanvas)) {
            TuioFXCanvas tuioFXCanvas = new TuioFXCanvas();
            Parent oldRoot = scene.getRoot();
            tuioFXCanvas.getChildren().addAll(new Node[]{oldRoot});
            scene.setRoot(tuioFXCanvas);
            oldRoot.setTranslateX(0.0);
            oldRoot.setTranslateY(0.0);
            oldRoot.getStyleClass().removeAll(new String[]{"root"});
        }

    }


    public static Node pickNode(Node node, double sceneX, double sceneY) {
        Point2D p = node.sceneToLocal(sceneX, sceneY, true /* rootScene */);

        // check if the given node has the point inside it, or else we drop out
        if (!node.contains(p)) return null;

        // at this point we know that _at least_ the given node is a valid
        // answer to the given point, so we will return that if we don't find
        // a better child option
        if (node instanceof Parent) {
            // we iterate through all children in reverse order, and stop when we find a match.
            // We do this as we know the elements at the end of the list have a higher
            // z-order, and are therefore the better match, compared to children that
            // might also intersect (but that would be underneath the element).
            Node bestMatchingChild = null;
            try {
                List<Node> children = ((Parent) node).getChildrenUnmodifiable();
                for (int i = children.size() - 1; i >= 0; i--) {
                    Node child = children.get(i);
                    p = child.sceneToLocal(sceneX, sceneY, true /* rootScene */);
                    if (child.isVisible() && !child.isMouseTransparent() && child.contains(p)) {
                        bestMatchingChild = child;
                        break;
                    }
                }
            } catch (Exception ignored) {

            }
            if (bestMatchingChild != null) {
                return pickNode(bestMatchingChild, sceneX, sceneY);
            }
        }

        return node;
    }
}
